# vim: filetype=sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.

#
# This is a ksh function library. It is intended to be sourced into
# other ksh scripts and not executed directly.
#

. ${STF_SUITE}/include/stf.shlib

#
# Send a debug message to stderr, if $STF_DEBUG set.
#
function log_debug
{
	[ -z "$STF_DEBUG" ] && return
	echo "$*" >&2
}

# Output an assertion
#
# $@ - assertion text

function log_assert
{
	_printline ASSERTION: "$@"
}

# Output a comment
#
# $@ - comment text

function log_note
{
	_printline NOTE: "$@"
}

# Execute a positive test and exit $STF_FAIL is test fails
#
# $@ - command to execute

function log_must
{
	log_pos "$@"
	(( $? != 0 )) && log_fail
}

# Execute a command that must exit $1
#
# $@ - command to execute
function log_mustbe
{
	typeset exitcode_wanted=$1
	shift

	log_cmd "$@"
	(( $? != $exitcode_wanted )) && log_fail
}

# Execute a negative test and exit $STF_FAIL if test passes
#
# $@ - command to execute

function log_mustnot
{
	log_neg "$@"
	(( $? != 0 )) && log_fail
}

# Execute a command that should only be logged if it fails.
#
# $@ - command to execute
function log_onfail
{
	eval $@
	typeset status=$?
	[ $status -eq 0 ] && return
	_printerror "$@" "unexpectedly exited $status"
}

# Execute and print command with status where success equals non-zero result
# or output includes expected keyword
#
# $2-$@ - command to execute
#
# Summary: execute $@.  Return 1 if any of the following hold:
#		1) The command exited 0, 127, 138, or 139
#		2) The command's stderr included "internal error" or
#		   "assertion failed"
#
# return 0 if command fails, or the output contains the keyword expected,
# return 1 otherwise

function log_neg
{
	typeset out=""
	typeset logfile="$TMPDIR/log.$$"
	typeset ret=1

	while [[ -e $logfile ]]; do
		logfile="$logfile.$$"
	done

	"$@" 2>$logfile
	typeset status=$?
	out="/bin/cat $logfile"

	# unexpected status
	if (( $status == 0 )); then
		 print -u2 $($out)
		_printerror "$@" "unexpectedly exited $status"
	# missing binary
	elif (( $status == 127 )); then
		print -u2 $($out)
		_printerror "$@" "unexpectedly exited $status (File not found)"
	# bus error - core dump
	elif (( $status == 138 )); then
		print -u2 $($out)
		_printerror "$@" "unexpectedly exited $status (Bus Error)"
	# segmentation violation - core dump
	elif (( $status == 139 )); then
		print -u2 $($out)
		_printerror "$@" "unexpectedly exited $status (SEGV)"
	else
		$out | /usr/bin/egrep -i "internal error|assertion failed" \
			> /dev/null 2>&1
		# internal error or assertion failed
		if (( $? == 0 )); then
			print -u2 $($out)
			_printerror "$@" "internal error or assertion failure" \
				" exited $status"
		else
			ret=0
		fi

		if (( $ret == 0 )); then
			[[ -n $LOGAPI_DEBUG ]] && print $($out)
			_printsuccess "$@" "exited $status"
		fi
	fi
	_recursive_output $logfile "false"
	return $ret
}

# Execute and print command; unconditionally return its exit code.
# Useful for code that needs to do more specialized exit status filtering.
function log_cmd
{
	typeset logfile="$TMPDIR/log.$$"

	while [[ -e $logfile ]]; do
		logfile="$logfile.$$"
	done

	"$@" 2>$logfile
	typeset status=$?
	_printline "EXECUTED (exited $status): $@"
	_recursive_output $logfile "false"
	return $status
}

# Execute and print command with status where success equals zero result
#
# $@ command to execute
#
# Summary: run $@.  return 1 if its exit status was nonzero or if it printed
#		 "internal error" or "assertion failed" to stderr.
#		print stderr on failure or if LOGAPI_DEBUG is set.
#
# return command exit status

function log_pos
{
	typeset out=""
	typeset logfile="$TMPDIR/log.$$"

	while [[ -e $logfile ]]; do
		logfile="$logfile.$$"
	done

	"$@" 2>$logfile
	typeset status=$?
	out="/bin/cat $logfile"

	if (( $status != 0 )) ; then
		print -u2 $($out)
		_printerror "$@" "exited $status"
	else
		$out | /usr/bin/egrep -i "internal error|assertion failed" \
			> /dev/null 2>&1
		# internal error or assertion failed
		if [[ $? -eq 0 ]]; then
			print -u2 $($out)
			_printerror "$@" "internal error or assertion failure" \
				" exited $status"
			status=1
		else
			[[ -n $LOGAPI_DEBUG ]] && print $($out)
			_printsuccess "$@"
		fi
	fi
	_recursive_output $logfile "false"
	return $status	
}

# Set an exit handler
#
# $@ - function(s) to perform on exit

function log_onexit
{
	_CLEANUP="$@"
}

#
# Exit functions
#

# Perform cleanup and exit $STF_PASS
#
# $@ - message text

function log_pass
{
	_endlog $STF_PASS "$@"
}

# Perform cleanup and exit $STF_FAIL
#
# $@ - message text

function log_fail
{
	_endlog $STF_FAIL "$@"
}

# Perform cleanup and exit $STF_UNRESOLVED
#
# $@ - message text

function log_unresolved
{
	_endlog $STF_UNRESOLVED "$@"
}

# Perform cleanup and exit $STF_NOTINUSE
#
# $@ - message text

function log_notinuse
{
	_endlog $STF_NOTINUSE "$@"
}

# Perform cleanup and exit $STF_UNSUPPORTED
#
# $@ - message text

function log_unsupported
{
	_endlog $STF_UNSUPPORTED "$@"
}

# Perform cleanup and exit $STF_UNTESTED
#
# $@ - message text

function log_untested
{
	_endlog $STF_UNTESTED "$@"
}

# Perform cleanup and exit $STF_UNINITIATED
#
# $@ - message text

function log_uninitiated
{
	_endlog $STF_UNINITIATED "$@"
}

# Perform cleanup and exit $STF_NORESULT
#
# $@ - message text

function log_noresult
{
	_endlog $STF_NORESULT "$@"
}

# Perform cleanup and exit $STF_WARNING
#
# $@ - message text

function log_warning
{
	_endlog $STF_WARNING "$@"
}

# Perform cleanup and exit $STF_TIMED_OUT
#
# $@ - message text

function log_timed_out
{
	_endlog $STF_TIMED_OUT "$@"
}

# Perform cleanup and exit $STF_OTHER
#
# $@ - message text

function log_other
{
	_endlog $STF_OTHER "$@"
}

#
# Internal functions
#

# Perform cleanup and exit 
#
# Summary:	Runs any cleanup routine registered with log_onexit.  Prints a
# 		message and exits $1.  Note: the _recursive_output does
# 		nothing, because the rest of this api guarantees that the
# 		logfile will not exist.
# $1 - stf exit code
# $2-$n - message text

function _endlog
{
	typeset logfile="$TMPDIR/log.$$"
	_recursive_output $logfile

	export STF_EXITCODE=$1
	shift
	(( ${#@} > 0 )) && _printline "$@"
	if [[ -n $_CLEANUP ]] ; then
		typeset cleanup=$_CLEANUP
		log_onexit ""
		log_note "Performing local cleanup via log_onexit ($cleanup)"
		$cleanup
	fi
	exit $STF_EXITCODE
}

# Output a formatted line
# 
# $@ - message text

function _printline
{
	print `/bin/date +%H:%M:%S` "$@"
}

# Output an error message
#
# $@ - message text

function _printerror
{
	_printline ERROR: "$@"
}

# Output a success message
#
# $@ - message text

function _printsuccess
{
	_printline SUCCESS: "$@"
}

# Output logfiles recursively
#
# $1 - start file
# $2 - indicate whether output the start file itself, default as yes.

function _recursive_output #logfile
{
	typeset logfile=$1

	while [[ -e $logfile ]]; do
		if [[ -z $2 || $logfile != $1 ]]; then
			/bin/cat $logfile
		fi
		/bin/rm -f $logfile
		logfile="$logfile.$$"
        done
}
